不管是學哪一種框架,todolist可以說是經典練習題,此次練習除了實現基本的功能新增與刪除之外,再加一個拖曳改變排序功能
雖然用到redux來處理todolist有點殺雞焉用牛刀,但就是拿來練習看看,本來以為很快就可以搞定一切,殊不知遇到一個很神秘的問題, 我刪除了第一個項目,但居然一口氣被刪了兩個,其實不只是刪除 ,連新增也是會執行兩次,查看觸發的function只有執行一遍,但是reducer的部分居然執行了兩次?為什麼?
透過估狗查到的解法是,將reducer獨立出來,不要放在原本的function component裡面,因為reducer的instance會隨著function component變化,將reducer抽離出來之後,恩..完全沒有反應,reducer還是觸發兩遍,最後的解決方法是移除React.StrictMode,如果用了StrictMode嚴格模式,那麼redux就會被多次的觸發,確保運作正常
There is no "problem". React intentionally calls your reducer twice to make any unexpected side effects more apparent. Since your reducer is pure, calling it twice doesn't affect the logic of your application. So you shouldn't worry about this.
In production, it will only be called once.
不過我不確定這是否為正規的做法,但為了讓程式可以正常運作,只能先摘掉StrictMode,如果有更好的做法歡迎告知
完整程式碼如下
import React, { useState, Fragment, useReducer } from 'react';
const initalState = {
list: ["sleep", "shopping"]
};
const reducer = (state, action) => {
switch (action.type) {
case "ADD_ITEM": {
return {
list: action.oldList
};
}
case "DELETE_ITEM": {
state.list.splice(action.index, 1);
return {
list: state.list
};
}
case "EXCHANGE_ITEM": {
const fromIndex = action.fromIndex;
const toIndex = action.toIndex;
const [del] = state.list.splice(fromIndex, 1);
state.list.splice(toIndex, 0, del);
return {
list: state.list
};
}
default: {
return state;
}
}
};
const useListReducer = () => {
return useReducer(reducer, initalState);
}
const ToDoList = () => {
const [state, dispatch] = useListReducer();
const [inputValue, setInputValue] = useState('')
const [BeginIndex, setaBeginIndex] = useState(0);
const deleteItem = (index) => {
dispatch({
type: "DELETE_ITEM",
index
});
}
const addItem = e => {
const value = e.target.value
if (e.key === 'Enter') {
if (!value) alert('請輸入代辦項目')
let oldList = [...state.list]
oldList.push(value)
dispatch({
type: "ADD_ITEM",
oldList
});
setInputValue('')
}
}
const handleChange = e => {
setInputValue(e.target.value);
};
return (
<Fragment>
<h3>新增代辦項目</h3>
<input value={inputValue} onChange={handleChange} onKeyDown={addItem} />
<ul>
{state.list.map((item, i) => {
return (
<ol
draggable='true'
onDragStart={() => {
setaBeginIndex(i)
}}
onDragEnter={() => {
dispatch({
type: "EXCHANGE_ITEM",
fromIndex: BeginIndex,
toIndex: i
});
setaBeginIndex(i)
}}
onDragEnd={
() => {
setaBeginIndex(undefined)
}}
key={i}
>
<span>{item}</span>
<button onClick={() => {
deleteItem(i)
}}>delete</button>
</ol>
)
})}
</ul>
</Fragment>
)
}
export default ToDoList
參考資料:useReducer Action dispatched twice